#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>

#include "ClassFile.h"
#include "MethodInfo.h"
#include "AstNode.h"
#include "CompileLlvm.h"
#include "CompileGraphVis.h"
#include "CompileJvm.h"

#define DEFAULT_OUTPUT_CLASSNAME "Sabine"
#define USE_VISITOR 1

extern int    yydebug;
static bool   dumpAst=false;
static char * outputClassName=0;
static bool   compLLVM=false;
static bool   compJVM=false;

static const char * source_filename;
int parse(
  const char * source_filename,
  AstNode* & out_ast);

enum {
  CMDLINE_ENABLE_BISON_DEBUG=100,
  CMDLINE_DUMP_AST,
  CMDLINE_OUTPUT_CLASS,
  CMDLINE_COMPILE_LLVM,
  CMDLINE_COMPILE_JVM,
  CMDLINE_USAGE,
};

static struct option long_options[] = {
  {"enable-bison-debug", 0, 0, CMDLINE_ENABLE_BISON_DEBUG},
  {"output-class", 1, 0, CMDLINE_OUTPUT_CLASS},
  {"dot", 0, 0, CMDLINE_DUMP_AST},
  {"llvm", 0, 0, CMDLINE_COMPILE_LLVM},
  {"jvm", 0, 0, CMDLINE_COMPILE_JVM},
  {"help", 0, 0, CMDLINE_USAGE},
  {0, 0, 0, 0}
};

static const char * options_help[]={
  "enable parser verbose output",
  "define output class default (default is " DEFAULT_OUTPUT_CLASSNAME ")",
  "dump abstract syntax tree as graphviz dot file",
  "compile versus \"llvm\"",
  "compile versus \"jvm\"",
  "print help and exit"
};

static void usage()
{
  struct option * opt;
  const char ** opt_help;
  for(opt=long_options, opt_help=options_help; opt->name; opt++, opt_help++) {
    printf("  --%s\n    %s\n",opt->name,*opt_help);
  }
}


int parse_command_line(int ac, char **av)
{
  int c;

  while (1) {
    //int this_option_optind = optind ? optind : 1;
    int option_index = 0;

    c = getopt_long(ac, av, "",
        long_options, &option_index);
    if (c == -1)
      break;

    switch (c) {
     case CMDLINE_USAGE:
       usage();
       exit(1);
       break;
     case CMDLINE_COMPILE_JVM:
        compJVM=true;
        break;
     case CMDLINE_COMPILE_LLVM:
        compLLVM=true;
        break;
     case CMDLINE_OUTPUT_CLASS:
        outputClassName=strdup(optarg);
        break;
      case CMDLINE_DUMP_AST:
        dumpAst=1;
        break;
      case CMDLINE_ENABLE_BISON_DEBUG:
        yydebug=1;
        break;
      case '?':
      default:
        printf("?? getopt returned character code 0%o ??\n", c);
    }
  }

  if(optind == ac) {
    printf("Fatal: a file to compile must be specified\n");
    return 1;
  }

  if(optind != ac-1) {
    printf("Fatal: only one file to be compiled can be specified\n");
    return 1;
  }

  source_filename=av[optind];

#if 0
  if (optind < ac) {
    printf("non-option ARGV-elements: ");
    while (optind < ac)
      printf("%s ", av[optind++]);
    printf("\n");
  } 
#endif
  return 0;
}

int main(int ac, char ** av)
{

  //yydebug=1;
  if(parse_command_line(ac,av)) {
    exit(1);
  }

  printf("Compiling: '%s'\n",source_filename);

  AstNode * ast;
  //ClassFile * klass;
  //MethodInfo * method;
  if(outputClassName==0)
    outputClassName=strdup(DEFAULT_OUTPUT_CLASSNAME);

  //klass=ClassFile::newClassFile(outputClassName);
  //method=klass->addDefaultMainMethod();

  if(parse(source_filename,ast)) {
    printf("****************** done parsing with errors\n");
    exit(1);
  } 
  CompileVisitor * visitor=0;
  if(dumpAst) {
    visitor=new CompileGraphViz(outputClassName);
  } else if(compLLVM) {
    // compiling for LLVM
    visitor=new CompileLlvm(outputClassName);
  } else {
    // compiling for the JVM
    visitor=new CompileJvm(outputClassName);
  }
  if(visitor) {
    ast->compile(visitor);
    visitor->finalize();
    delete visitor;
  } else {
    printf("Nothing to do\n");
  }
  free(outputClassName);
  exit(0);
}
